package com.csap.framework.validation;

import com.csap.framework.annotation.apidoc.ApiModel;
import com.csap.framework.annotation.apidoc.ApiOperation;
import com.csap.framework.extension.ThreadLocalUtil;
import com.csap.framework.properties.apidoc.ModelTypepProperties;
import com.csap.framework.util.ClazzUtils;
import com.csap.framework.util.CollectionsUtil;
import com.csap.framework.util.FinalConfigParam;
import com.csap.framework.util.StringUtil;
import com.csap.framework.util.TypeVariableModel;
import com.csap.framework.util.json.JsonUtilFastjson;
import com.csap.framework.validation.factory.ValidateFactory;
import com.csap.mybatisplus.page.OrderItem;
import com.csap.mybatisplus.page.Page;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author yangchengfu
 * @description 统一验证
 * @dataTime 2020年-01月-04日 22:13:00
 **/
@Aspect
@Slf4j
public final class ValidateIntercept {
    private ValidateFactory validateFactory;
    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    public ValidateIntercept(ValidateFactory validateFactory) {
        this.validateFactory = validateFactory;
    }

    /**
     * 方法执行前验证
     *
     * @param point
     */
    @Before("@annotation(com.csap.framework.annotation.apidoc.ApiOperation)")
    public void validate(JoinPoint point) {
        //获取请求的当前方法
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        TypeVariableModel typeVariableModel = new TypeVariableModel(point.getSourceLocation().getWithinType());
        ThreadLocalUtil.set(ThreadLocalUtil.typeVariableModel, typeVariableModel);
        if (method.isAnnotationPresent(ApiOperation.class)) {
            //参数
            Object[] objects = point.getArgs();
            Type[] cl = method.getGenericParameterTypes();
            sortColumn(typeVariableModel);
            String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
            for (int x = 0; x < cl.length; x++) {
                ModelTypepProperties properties = ClazzUtils.getRawClassType(cl[x], typeVariableModel);
                String type = properties.getRawType();
                if (!validateFactory.getClObj(type)) {
                    continue;
                }
                String parameterName = parameterNames[x];
                Object object = objects[x];
                if (FinalConfigParam.DATA_TYPE.contains(type) || FinalConfigParam.OTHER_DATA_TYPE.contains(type)) {
                    validateFactory.validation(object, signature, point.getSourceLocation().getWithinType(), properties.getCl(), parameterName);
                } else if (FinalConfigParam.list.contains(type)) {
                    if (FinalConfigParam.DATA_TYPE.contains(properties.getCl().getName()) || FinalConfigParam.OTHER_DATA_TYPE.contains(type)) {
                        validateFactory.validation(object, signature, point.getSourceLocation().getWithinType(), properties.getCl(), parameterName);
                    } else {
                        if (CollectionsUtil.isNotEmpty((Collection) object)) {
                            ((Collection) object).forEach(item -> validateFactory.validation(item, signature, point.getSourceLocation().getWithinType(), properties.getCl(), parameterName));
                        }
                    }

                } else if (properties.getCl().isAnnotationPresent(ApiModel.class)) {
                    validateFactory.validation(object, signature, point.getSourceLocation().getWithinType(), properties.getCl(), parameterName);
                }
            }
            if (log.isDebugEnabled()) {
                try {
                    ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                    String descr = "";
                    if (apiOperation != null) {
                        descr = apiOperation.value();
                    }
                    log.debug("method {} description {} request paramType {} paramsName {} data {}", method.getName(), descr,
                            Stream.of(cl).map(Type::getTypeName).collect(Collectors.toList())
                            , parameterNameDiscoverer.getParameterNames(method), JsonUtilFastjson.toJSONString(Stream.of(objects).filter(i -> !(i instanceof ServletRequest || i instanceof ServletResponse || i instanceof MultipartFile)).collect(Collectors.toList()), "files", "file"));
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 处理排序字段
     *
     * @param typeVariableModel
     */
    private void sortColumn(TypeVariableModel typeVariableModel) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (servletRequestAttributes == null) {
            log.warn("RequestContextHolder.getRequestAttributes() result value is null");
            return;
        }
        HttpServletRequest request = servletRequestAttributes.getRequest();
        if (request == null) {
            log.warn("RequestContextHolder.getRequestAttributes() getRequest is null");
            return;
        }
        String[] column = request.getParameterValues("column");
        String[] asc = request.getParameterValues("asc");
        String size = request.getParameter("size");
        String pageSize = request.getParameter("pageSize");
        String current = request.getParameter("current");
        String currentPage = request.getParameter("currentPage");
        Page page = ThreadLocalUtil.get(ThreadLocalUtil.page);
        if (page == null) {
            page = ThreadLocalUtil.set(ThreadLocalUtil.page, new Page<>());
        }
        if (CollectionsUtil.isNotEmpty(column)) {
            for (int x = 0; x < column.length; x++) {
                String c = column[x];
                try {
                    if (asc.length > x) {
                        page.addOrder(Boolean.parseBoolean(asc[x]) ? OrderItem.asc(c) : OrderItem.desc(c));
                    } else {
                        page.addOrder(OrderItem.desc(c));
                    }
                } catch (Exception e) {
                    page.addOrder(OrderItem.desc(c));
                }
            }
        }
        if (StringUtil.isNotEmpty(current) || StringUtil.isNotEmpty(currentPage)) {
            try {
                page.setCurrent(((StringUtil.isNotEmpty(current) ? Long.parseLong(current) : Long.parseLong(currentPage))));
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        if (StringUtil.isNotEmpty(size) || StringUtil.isNotEmpty(pageSize)) {
            try {
                page.setSize(((StringUtil.isEmpty(size) ? Long.parseLong(pageSize) : Long.parseLong(size))));
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }

    }

    @After("@annotation(com.csap.framework.annotation.apidoc.ApiOperation)")
    public void After(JoinPoint point) {
        ThreadLocalUtil.remove(ThreadLocalUtil.page);
        ThreadLocalUtil.remove(ThreadLocalUtil.D_TIME_ZONME);
        ThreadLocalUtil.remove(ThreadLocalUtil.typeVariableModel);
    }
}
